use core::fmt;
use jsonwebtoken::{
    decode, encode, errors::ErrorKind, DecodingKey, EncodingKey, Header, Validation,
};
use serde::{Deserialize, Serialize};
use std::fmt::{Display, Formatter};

use crate::error::Error;

#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct JWTClaims {
    pub id: String,
    pub account: String,
    pub permissions: Vec<String>,
    pub role_ids: Vec<String>,
    pub exp: usize,
}

impl JWTClaims {
    // 生成token
    pub fn generate_token(&self, secret: &str) -> Result<String, Error> {
        let header = Header::new(jsonwebtoken::Algorithm::HS512);
        return match encode(&header, self, &EncodingKey::from_secret(secret.as_ref())) {
            Ok(t) => Ok(t),
            Err(_) => Err(Error::from("JWTToken encode fail!")), // in practice you would return the error
        };
    }

    // 验证token
    pub fn verify(secret: &str, token: &str) -> Result<JWTClaims, Error> {
        let mut validation = Validation::new(jsonwebtoken::Algorithm::HS512);
        validation.leeway = 0;
        return match decode::<JWTClaims>(
            &token,
            &DecodingKey::from_secret(secret.as_ref()),
            &validation,
        ) {
            Ok(c) => Ok(c.claims),
            Err(err) => match *err.kind() {
                ErrorKind::InvalidToken => return Err(Error::from("InvalidToken")), // Example on how to handle a specific error
                ErrorKind::InvalidIssuer => return Err(Error::from("InvalidIssuer")), // Example on how to handle a specific error
                ErrorKind::ExpiredSignature => return Err(Error::from("ExpiredSignature")),
                _ => return Err(Error::from("InvalidToken other errors")),
            },
        };
    }

    // 刷新token
    pub fn refresh(&self, secret: &str, jwt_exp: usize) -> Result<String, Error> {
        let mut jwt = self.clone();
        jwt.exp = jwt.exp + jwt_exp;
        jwt.generate_token(&secret)
    }
}

#[derive(Debug)]
pub struct TokenError;

impl Display for TokenError {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "Invalid token")
    }
}
